/**
 * 
 */
package org.vsg.stock.basic.service.business;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Vector;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vsg.common.biz.exception.ServiceException;
import org.vsg.stock.basic.domain.BalanceLine;
import org.vsg.stock.basic.domain.BalanceLineFactory;
import org.vsg.stock.basic.domain.BalanceLineKeyPoint;
import org.vsg.stock.basic.service.StockBusinessHelperManager;
import org.vsg.stock.basic.service.StockBusinessHelperManager.BlurRangeType;
import org.vsg.stock.core.domain.StockDailyKLine;
import org.vsg.stock.core.indicator.KLineStockIndicatorModel;
import org.vsg.stock.core.indicator.ZigIndicator;
import org.vsg.stock.core.indicator.kline.ZigIndicatorResultItem;

/**
 * 算法逻辑
 * 1. 分别找出峰谷关联的对像价格
 * 2. 将价格拉出来,进行合并
 * 3. 然后根据价格,每次扫描最大可能关联的对像
 * 4. 计算各自关联的数量的大小
 * 5. *关键: 根据当前价格 ，找到关联内的价格范围，根据多个价格中，找出最接近基线的对像价格
 * 5. 由大到小价格一个个提取关联的对像，提取一个去掉一个，以当前价格进行分析
 * 6. 得到的价格峰谷需由量柱的峰谷再得到证明 ，只要价格中对应的周期能满足就可以
 * @author Bill Vison
 *
 */
public class CrestTroughTogeterBalanceLineFinderBO extends
	OrgDataStockBalanceLineFinderBO {

	private static final Logger logger = LoggerFactory.getLogger(CrestTroughTogeterBalanceLineFinderBO.class);

	private BalanceLineFactory factory = BalanceLineFactory.getInstance();
	
	private static final String FINDER_CODE = "crest_trough_balance_line";
	
	@Override
	public String getFinderCode() {
		// TODO Auto-generated method stub
		return FINDER_CODE;
	}	
	
	@Override
	protected List<BalanceLine> scanLines(List<StockDailyKLine>  stkKlineList) {

		// --- scan and mark point in to list and map ---

		ZigIndicator zigInd = new ZigIndicator();
		// 过细,将无法分出波,过大找的波较少,对于价来说, 3-3.3为合适
		

		Map<String, java.io.Serializable > resultMap = null;
		
		// --- pick out all the crests ---
		zigInd.setIndicatorModel( new KLineStockIndicatorModel(stkKlineList ,KLineStockIndicatorModel.KLinePosition.HIGH) );
		zigInd.setThreshold( (double)3.2 );
		resultMap  = zigInd.calculate();		
		List<ZigIndicatorResultItem> highCrests = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_CREST );

		zigInd.setIndicatorModel( new KLineStockIndicatorModel(stkKlineList ,KLineStockIndicatorModel.KLinePosition.CLOSE) );
		//zigInd.setThreshold( (double)2.19999 );
		zigInd.setThreshold( (double)3.2 );
		resultMap  = zigInd.calculate();		
		List<ZigIndicatorResultItem> closeCrests = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_CREST );
		List<ZigIndicatorResultItem> closeTroughs = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_TROUGH );		
		
		// --- pick out all the trough ---
		zigInd.setIndicatorModel( new KLineStockIndicatorModel(stkKlineList ,KLineStockIndicatorModel.KLinePosition.LOW) );
		zigInd.setThreshold( (double)3.2 );
		resultMap  = zigInd.calculate();		
		List<ZigIndicatorResultItem> lowTroughs = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_TROUGH );

		
		// --- pick out all the trough ---
		zigInd.setIndicatorModel( new KLineStockIndicatorModel(stkKlineList ,KLineStockIndicatorModel.KLinePosition.OPEN) );
		zigInd.setThreshold( (double)3.2 );
		resultMap  = zigInd.calculate();		
		List<ZigIndicatorResultItem> openTroughs = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_TROUGH );		
		List<ZigIndicatorResultItem> openCrests = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_CREST );		

		// ---- analyze the vol crests and troughs -----
		KLineStockIndicatorModel klineStkIndiModel = new KLineStockIndicatorModel(stkKlineList ,KLineStockIndicatorModel.KLinePosition.VOL);
		zigInd.setThreshold( (double)15 );
		zigInd.setIndicatorModel( klineStkIndiModel );		
		resultMap  = zigInd.calculate();
		List<ZigIndicatorResultItem> volCrests = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_CREST );
		List<ZigIndicatorResultItem> volTroughs = (List<ZigIndicatorResultItem>) resultMap.get( ZigIndicator.ALL_TROUGH );
		
		
		List<BalanceLine> allNewLines = new Vector<BalanceLine>();			
		
		// --- merge to togeter vo ----
		Set<CrestTroughTogetherTypeVO> togetherCTSet = groupingAllToTogether(highCrests ,
				closeCrests ,
				lowTroughs , openTroughs, closeTroughs , openCrests , volCrests , volTroughs );

		if ( logger.isDebugEnabled()) {
			List<CrestTroughTogetherTypeVO> sortList = new LinkedList<CrestTroughTogetherTypeVO>(togetherCTSet);
			
			Collections.sort( sortList , new Comparator<CrestTroughTogetherTypeVO>() {

				@Override
				public int compare(CrestTroughTogetherTypeVO arg0,
						CrestTroughTogetherTypeVO arg1) {
					
					if ( arg0.getDate().after( arg1.getDate() ) ) {
						return 1;
					} else if (  arg0.getDate().before(arg1.getDate() ) ) {
						return -1;
					}

					return 0;
				}
				
			});
			
			for (CrestTroughTogetherTypeVO vo : sortList) {
				System.out.println(vo);				
			}
		}
		
		
		try {
			
			Map<Double , Set<CrestTroughTogetherTypeVO>>  ctTogetherTypeMap = pickOutAllTogetherItemByPrice(togetherCTSet);
	
			// ---- merge to simple map ----
			ctTogetherTypeMap = filterTogether( ctTogetherTypeMap );


			// --- build group line & point ---
			Entry<Double, Set<CrestTroughTogetherTypeVO>> entry = null;
			
			for (Iterator<Entry<Double, Set<CrestTroughTogetherTypeVO>>> iter = ctTogetherTypeMap.entrySet().iterator() ; iter.hasNext(); ) {
				
				entry = iter.next();
				
				Set<CrestTroughTogetherTypeVO> values = entry.getValue();
				
				BalanceLine aNewLine = factory.createLine( getStockCode(), BalanceLine.FORM_H);
				aNewLine.setForm(BalanceLine.FORM_H);
				for (Iterator<CrestTroughTogetherTypeVO> klineIter = values.iterator() ; klineIter.hasNext(); ) {
					CrestTroughTogetherTypeVO crestTroughTogetherVO = klineIter.next();
					
					BalanceLineKeyPoint blkPoint = factory.createPoint(crestTroughTogetherVO.getPrice(), crestTroughTogetherVO.getKline());
					
					aNewLine.addKeyPoint( blkPoint );				
				}
				allNewLines.add(aNewLine);			

			}

		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		



		return allNewLines;
	}

	
	private Map<Double , Set<CrestTroughTogetherTypeVO>> filterTogether(Map<Double , Set<CrestTroughTogetherTypeVO>> input) {
		
		Set<Map.Entry<Double,Set<CrestTroughTogetherTypeVO>>>  entries =  input.entrySet();
		
		Set<CrestTroughTogetherTypeVO> crestTroughTogetherSet = null;

		for (Iterator<Map.Entry<Double,Set<CrestTroughTogetherTypeVO>>>  entriesIter = entries.iterator(); entriesIter.hasNext(); ) {
			Map.Entry<Double,Set<CrestTroughTogetherTypeVO>> entry = entriesIter.next();
			
			crestTroughTogetherSet = entry.getValue();
			
			boolean hasACrest = false;
			boolean hasATrough = false;
			for (CrestTroughTogetherTypeVO vo : crestTroughTogetherSet) {
				if (vo.getPriceZigItemType() == ZigIndicatorResultItem.Type.CREST
					&& vo.getVolZigItemType() == ZigIndicatorResultItem.Type.CREST) {
					hasACrest = true;
				}
				else if (vo.getPriceZigItemType() == ZigIndicatorResultItem.Type.TROUGH
						&& vo.getVolZigItemType() == ZigIndicatorResultItem.Type.TROUGH) {
					hasATrough = true;
				}				
			}
			
			if ( ! (hasACrest && hasATrough)  ) {
				entriesIter.remove();
			}
		}
		
		// --- sort the closely line ---
		LinkedList<Double> keyPrices = new LinkedList<Double>( input.keySet() );
		Collections.sort( keyPrices );
		
		LinkedHashSet<Double> usedToPriceSet = new LinkedHashSet<Double>();
		
		LinkedHashSet<Double> newPriceSet = new LinkedHashSet<Double>();

		for (Iterator<Double> keyIter = keyPrices.iterator() ; keyIter.hasNext() ;  ) {
			
			LinkedHashSet<Double> chooseClosePriceSet = new LinkedHashSet<Double>();
			
			double selectedPrice = keyIter.next();
			
			if (usedToPriceSet.contains( selectedPrice ) ) {
				continue;
			}
			
			chooseClosePriceSet.add( selectedPrice );
			
			double selectedPriceMax = sdkHelperManager.getStockBlurLimitedPrice(selectedPrice , BlurRangeType.MAX, this.getStockCode());
			double selectedPriceMin = sdkHelperManager.getStockBlurLimitedPrice(selectedPrice , BlurRangeType.MIN, this.getStockCode());
			
			Set<CrestTroughTogetherTypeVO>  selectedSet = input.get( selectedPrice );
			boolean isCloseSet = false;			
			for (Iterator<Double> subKeyIter = keyPrices.iterator() ; subKeyIter.hasNext() ;  ) {

				double comparedPrice = subKeyIter.next();
				
				if (selectedPrice == comparedPrice) {
					continue;
				}
				
				if (comparedPrice >= selectedPriceMin && comparedPrice <= selectedPriceMax ) {
					// --- check link the price ---
					Set<CrestTroughTogetherTypeVO>  comparedSet = input.get( comparedPrice );
					isCloseSet = selectedSet.equals( comparedSet );
					chooseClosePriceSet.add( comparedPrice );
				}
				
			}
			double closelyPrice = 0;
			if ( isCloseSet ) {
				// --- select the close price ---
				closelyPrice = pickOutClosePrice(chooseClosePriceSet , selectedSet);
				usedToPriceSet.addAll( chooseClosePriceSet );
			
			}
			if (closelyPrice != 0) {
				newPriceSet.add( closelyPrice );
			} else {
				newPriceSet.add( selectedPrice );
			}
		}
		
		// --- remove entry ---
		for (Iterator<Map.Entry<Double,Set<CrestTroughTogetherTypeVO>>>  entriesIter = entries.iterator(); entriesIter.hasNext(); ) {
			Map.Entry<Double,Set<CrestTroughTogetherTypeVO>> entry = entriesIter.next();

			if ( !newPriceSet.contains( entry.getKey() )  ) {
				entriesIter.remove();
			}
		}

		
		return input;
	}
	
	
	private double pickOutClosePrice(LinkedHashSet<Double> allCompared , Set<CrestTroughTogetherTypeVO> selectSet) {
		
		double returnMarked = 0;
		double lastLessDifference = 999999;
		for (Iterator<Double> compIter = allCompared.iterator() ; compIter.hasNext() ; ) {
			
			double currentPrice = compIter.next(); 
			double subTotal = 0;
			for (Iterator<CrestTroughTogetherTypeVO> compSetIter = selectSet.iterator() ; compSetIter.hasNext() ; ) {
				double subPrice = Math.abs( compSetIter.next().getPrice() - currentPrice);
				subTotal += subPrice;
			}
			
			if (lastLessDifference > subTotal) {
				returnMarked = currentPrice;
				lastLessDifference = subTotal;
			}
		}
		return returnMarked;
	}
		
	
	
	/**
	 * seperate the together type by price
	 * @param crestTroughTogeterTypeSet
	 * @return
	 */
	private Map<Double , Set<CrestTroughTogetherTypeVO>> pickOutTogetherItemByPrice(Set<CrestTroughTogetherTypeVO> crestTroughTogeterTypeSet ) {
		
		Map<Double , Set<CrestTroughTogetherTypeVO>> priceMap = new HashMap<Double , Set<CrestTroughTogetherTypeVO>>();
		
		// --- find merge price --
		Iterator<CrestTroughTogetherTypeVO> resultItemsIter = crestTroughTogeterTypeSet.iterator();
		StockDailyKLine kline = null;
		while ( resultItemsIter.hasNext() ) {
			
			CrestTroughTogetherTypeVO typeVo = resultItemsIter.next();
			kline = typeVo.getKline();
			
			try {
				
				Map<Double,String> mergePriceRecord = sdkHelperManager.mergeToPriceMap(kline , kline.getId().getStockCode());
				Set<Double> mergePriceKeies = mergePriceRecord.keySet();		
				for (double mergePrice : mergePriceKeies) {
					
					Set<CrestTroughTogetherTypeVO> listContainer = priceMap.get(mergePrice);

					if (listContainer == null) {
						 listContainer = new LinkedHashSet<CrestTroughTogetherTypeVO>();
					}
					listContainer.add( typeVo );
					
					priceMap.put(mergePrice, listContainer);
				}
				
			} catch (ServiceException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}			
		}
		
		return priceMap;
	}	
	

	
	/**
	 *  group and defined all 
	 * @return
	 */
	private Set<CrestTroughTogetherTypeVO> groupingAllToTogether(
			List<ZigIndicatorResultItem> highCrests,
			List<ZigIndicatorResultItem> closeCrests,
			List<ZigIndicatorResultItem> lowTroughs ,
			List<ZigIndicatorResultItem> openTroughs,
			List<ZigIndicatorResultItem> closeTroughs,
			List<ZigIndicatorResultItem> openCrests,
			List<ZigIndicatorResultItem> volCrests,
			List<ZigIndicatorResultItem> volTroughs
	) {
		
		Set<CrestTroughTogetherTypeVO> crestTroughTogeterType = new LinkedHashSet<CrestTroughTogetherTypeVO>();
		
		
		// ---- prepare vol crest date ----
		LinkedList<Date> volCrestsDates = new LinkedList<Date>();
		
		for (ZigIndicatorResultItem volCrest : volCrests) {
			StockDailyKLine kline = (StockDailyKLine)volCrest.getAssoRef();
			volCrestsDates.add( kline.getDate() );
		}

		// ---- prepare vol troughts date ----
		LinkedList<Date> volTroughsDates = new LinkedList<Date>();
		for (ZigIndicatorResultItem volTrough : volTroughs) {
			StockDailyKLine kline = (StockDailyKLine)volTrough.getAssoRef();
			volTroughsDates.add( kline.getDate() );
		}
		
		// ---- find high crest map ----
		for (ZigIndicatorResultItem item : highCrests) {
			StockDailyKLine kline = (StockDailyKLine)item.getAssoRef();
			Date klineDate = kline.getDate();
			
			// --- build object ---
			CrestTroughTogetherTypeVO cttTypeVo = new CrestTroughTogetherTypeVO();
			cttTypeVo.setDate( klineDate );
			cttTypeVo.setPrice( item.getValue() );
			cttTypeVo.setPriceType( StockDailyKLine.HIGH );
			cttTypeVo.setPriceZigItemType( item.getType() );
			cttTypeVo.setKline( kline );			
			
			int indexForCrestDates = volCrestsDates.indexOf( klineDate );
			if (indexForCrestDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volCrest = volCrests.get(indexForCrestDates);
				cttTypeVo.setVolZigItemType( volCrest != null ?  volCrest.getType() : null );
			}
			int indexForTroughDates = volTroughsDates.indexOf( klineDate );
			if (indexForTroughDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volTrough = volTroughs.get(indexForTroughDates);
				cttTypeVo.setVolZigItemType( volTrough != null ?  volTrough.getType() : null );
			}			

			crestTroughTogeterType.add( cttTypeVo );
		}		

		// ---- find close crest map ----
		for (ZigIndicatorResultItem item : closeCrests) {
			StockDailyKLine kline = (StockDailyKLine)item.getAssoRef();
			Date klineDate = kline.getDate();
			
			// --- build object ---
			CrestTroughTogetherTypeVO cttTypeVo = new CrestTroughTogetherTypeVO();
			cttTypeVo.setDate( klineDate );
			cttTypeVo.setPrice( item.getValue() );
			cttTypeVo.setPriceType( StockDailyKLine.CLOSE );
			cttTypeVo.setPriceZigItemType( item.getType() );
			cttTypeVo.setKline( kline );			

			
			int indexForCrestDates = volCrestsDates.indexOf( klineDate );
			if (indexForCrestDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volCrest = volCrests.get(indexForCrestDates);
				cttTypeVo.setVolZigItemType( volCrest != null ?  volCrest.getType() : null );
			}
			int indexForTroughDates = volTroughsDates.indexOf( klineDate );
			if (indexForTroughDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volTrough = volTroughs.get(indexForTroughDates);
				cttTypeVo.setVolZigItemType( volTrough != null ?  volTrough.getType() : null );
			}			
			
			
			crestTroughTogeterType.add( cttTypeVo );
		}		

		
		// ---- find close crest map ----
		for (ZigIndicatorResultItem item : closeTroughs) {
			StockDailyKLine kline = (StockDailyKLine)item.getAssoRef();
			Date klineDate = kline.getDate();
			
			// --- build object ---
			CrestTroughTogetherTypeVO cttTypeVo = new CrestTroughTogetherTypeVO();
			cttTypeVo.setDate( klineDate );
			cttTypeVo.setPrice( item.getValue() );
			cttTypeVo.setPriceType( StockDailyKLine.CLOSE );
			cttTypeVo.setPriceZigItemType( item.getType() );
			cttTypeVo.setKline( kline );			

			
			int indexForCrestDates = volCrestsDates.indexOf( klineDate );
			if (indexForCrestDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volCrest = volCrests.get(indexForCrestDates);
				cttTypeVo.setVolZigItemType( volCrest != null ?  volCrest.getType() : null );
			}
			int indexForTroughDates = volTroughsDates.indexOf( klineDate );
			if (indexForTroughDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volTrough = volTroughs.get(indexForTroughDates);
				cttTypeVo.setVolZigItemType( volTrough != null ?  volTrough.getType() : null );
			}			
			
			
			crestTroughTogeterType.add( cttTypeVo );
		}		

		
		
		// ---- find low troughs map ----
		for (ZigIndicatorResultItem item : lowTroughs) {
			StockDailyKLine kline = (StockDailyKLine)item.getAssoRef();
			Date klineDate = kline.getDate();
			
			// --- build object ---
			CrestTroughTogetherTypeVO cttTypeVo = new CrestTroughTogetherTypeVO();
			cttTypeVo.setDate( klineDate );
			cttTypeVo.setPrice( item.getValue() );
			cttTypeVo.setPriceType( StockDailyKLine.LOW );
			cttTypeVo.setPriceZigItemType( item.getType() );
			cttTypeVo.setKline( kline );
			
			int indexForCrestDates = volCrestsDates.indexOf( klineDate );
			if (indexForCrestDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volCrest = volCrests.get(indexForCrestDates);
				cttTypeVo.setVolZigItemType( volCrest != null ?  volCrest.getType() : null );
			}
			int indexForTroughDates = volTroughsDates.indexOf( klineDate );
			if (indexForTroughDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volTrough = volTroughs.get(indexForTroughDates);
				cttTypeVo.setVolZigItemType( volTrough != null ?  volTrough.getType() : null );
			}
			crestTroughTogeterType.add( cttTypeVo );
		}		

		// ---- find open crest map ----
		for (ZigIndicatorResultItem item : openTroughs) {
			StockDailyKLine kline = (StockDailyKLine)item.getAssoRef();
			Date klineDate = kline.getDate();

			
			// --- build object ---
			CrestTroughTogetherTypeVO cttTypeVo = new CrestTroughTogetherTypeVO();
			cttTypeVo.setDate( klineDate );
			cttTypeVo.setPrice( item.getValue() );
			cttTypeVo.setPriceType( StockDailyKLine.OPEN );
			cttTypeVo.setPriceZigItemType( item.getType() );
			cttTypeVo.setKline( kline );			
			
			int indexForCrestDates = volCrestsDates.indexOf( klineDate );
			if (indexForCrestDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volCrest = volCrests.get(indexForCrestDates);
				cttTypeVo.setVolZigItemType( volCrest != null ?  volCrest.getType() : null );
			}
			int indexForTroughDates = volTroughsDates.indexOf( klineDate );
			if (indexForTroughDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volTrough = volTroughs.get(indexForTroughDates);
				cttTypeVo.setVolZigItemType( volTrough != null ?  volTrough.getType() : null );
			}
			
			crestTroughTogeterType.add( cttTypeVo );
		}		
		
		
		// ---- find open crest map ----
		for (ZigIndicatorResultItem item : openCrests) {
			StockDailyKLine kline = (StockDailyKLine)item.getAssoRef();
			Date klineDate = kline.getDate();

			// --- build object ---
			CrestTroughTogetherTypeVO cttTypeVo = new CrestTroughTogetherTypeVO();
			cttTypeVo.setDate( klineDate );
			cttTypeVo.setPrice( item.getValue() );
			cttTypeVo.setPriceType( StockDailyKLine.OPEN );
			cttTypeVo.setPriceZigItemType( item.getType() );
			cttTypeVo.setKline( kline );			
			
			int indexForCrestDates = volCrestsDates.indexOf( klineDate );
			if (indexForCrestDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volCrest = volCrests.get(indexForCrestDates);
				cttTypeVo.setVolZigItemType( volCrest != null ?  volCrest.getType() : null );
			}
			int indexForTroughDates = volTroughsDates.indexOf( klineDate );
			if (indexForTroughDates > -1) {
				// --- find the url ---
				ZigIndicatorResultItem volTrough = volTroughs.get(indexForTroughDates);
				cttTypeVo.setVolZigItemType( volTrough != null ?  volTrough.getType() : null );
			}
			
			crestTroughTogeterType.add( cttTypeVo );
		}		

		
		return crestTroughTogeterType;
	}

	
	

	


	

	private Map<Double , Set<CrestTroughTogetherTypeVO>> pickOutAllTogetherItemByPrice (Set<CrestTroughTogetherTypeVO> crestTroughTogeterTypeSet )  throws ServiceException {

		Map<Double , Set<CrestTroughTogetherTypeVO>> toItemPriceMap =  pickOutTogetherItemByPrice(crestTroughTogeterTypeSet);
		
		List<Double> listOfPrice = new Vector<Double>( toItemPriceMap.keySet() );
		Collections.sort(listOfPrice);
		
		Map<Double, Set<CrestTroughTogetherTypeVO>> tmpTogetherMap = new LinkedHashMap<Double, Set<CrestTroughTogetherTypeVO>>();
		Map<Double,Integer> togetherCounterMap = new LinkedHashMap<Double,Integer>();
		// --- get merge price ---
		for (double comparePrice : listOfPrice) {
			
			double[] matchPrices = searchMatchPrices(this.getStockCode() , comparePrice , listOfPrice);
			
			Set<CrestTroughTogetherTypeVO> togeterSet = new LinkedHashSet<CrestTroughTogetherTypeVO>();
			
			for (double matchPrice : matchPrices) {
				
				Set<CrestTroughTogetherTypeVO> crtrTogetherTypeSet = toItemPriceMap.get( matchPrice );
				
				togeterSet.addAll( crtrTogetherTypeSet );

			}

			// --- remove all kline not owned crests and troughts
			togeterSet = removeNotTogeterKLine(togeterSet);				
			
			// --- put all list to set ---
			if ( togeterSet != null) {
				tmpTogetherMap.put( comparePrice , togeterSet);
				togetherCounterMap.put(comparePrice, togeterSet.size());
			}
		}

		// --- counter the close price and sort price list ----
		// --- sort by counter value ---
		togetherCounterMap = sortPriceMapCounter(togetherCounterMap);
		
		List<Double> priceList = new Vector<Double>( togetherCounterMap.keySet() ) ;
		
		Set<Entry<Double, Integer>> entriesSet = togetherCounterMap.entrySet();
		Entry<Double, Integer> entry = null;
		
		// --- return result mapping ---
		Map<Double , Set<CrestTroughTogetherTypeVO>> outputPriceMapping = new LinkedHashMap<Double , Set<CrestTroughTogetherTypeVO>>();
		
		for (Iterator<Entry<Double, Integer>>  entriesIter = entriesSet.iterator() ; entriesIter.hasNext(); ) {
			entry = entriesIter.next();
			double entryPrice = entry.getKey() ;
			double[] morePrice = searchMatchPrices(this.getStockCode(), entryPrice , priceList);
			
			// --- 除本身外,取最接近的 两个差别值 |level - price| ----
			double[] morePriceClosely = pickOutcloselyPriceGrouping(entryPrice , morePrice);

			LinkedHashSet<CrestTroughTogetherTypeVO> tmpSet = new LinkedHashSet<CrestTroughTogetherTypeVO>();
			for (double comparedPrice : morePriceClosely) {
				tmpSet.addAll(  tmpTogetherMap.get( comparedPrice ) );
			}			
			
			double selectedPrice = analyzeCloselyPriceFromPriceGrouping(morePriceClosely , tmpSet);
			double selectedPriceMax = sdkHelperManager.getStockBlurLimitedPrice(selectedPrice , BlurRangeType.MAX, this.getStockCode());
			double selectedPriceMin = sdkHelperManager.getStockBlurLimitedPrice(selectedPrice , BlurRangeType.MIN, this.getStockCode());
			
			// --- select pick list ---
			Set<CrestTroughTogetherTypeVO> selectedItems = new LinkedHashSet<CrestTroughTogetherTypeVO>();
			for (CrestTroughTogetherTypeVO kline : tmpSet) {
				
				if (kline.getPrice() <= selectedPriceMax && kline.getPrice() >= selectedPriceMin) {
					// --- count for date mapping --- 
					selectedItems.add(  kline );
				}				
			}
			// --- clear all object ---
			tmpSet.clear();
			tmpSet = null;
			
			// --- clear all the pick out item ---
			
			if (selectedPrice != 0) {
				// ---- add the mapping ---
				outputPriceMapping.put(  selectedPrice , selectedItems);
			}
			
		}

		
		return outputPriceMapping;
	}
	
	/**
	 * remove no has crest and trought 
	 * @param zigItemsSet
	 * @return
	 */
	private Set<CrestTroughTogetherTypeVO> removeNotTogeterKLine(Set<CrestTroughTogetherTypeVO> zigItemsSet ) {
		
		boolean hasCrests = false;
		
		boolean hasTroughs = false;
		
		for (CrestTroughTogetherTypeVO typeVo : zigItemsSet) {
			
			
			if (!hasCrests) {
				
				if (typeVo.getPriceZigItemType() == ZigIndicatorResultItem.Type.CREST ) {
					hasCrests = true;
				}
			}
			
			if (!hasTroughs) {
				
				if (typeVo.getPriceZigItemType() == ZigIndicatorResultItem.Type.TROUGH ) {
					hasTroughs = true;
				}
			}
			
		}
		
		if (hasCrests && hasTroughs) {
			return zigItemsSet;
		} else {
			return null;
		}
		
	}
		
	
	

	
	/**
	 * 1. 先找出各自关系价格出可以接受的元素,组合成一个集合多个关联的对像
	 * 2. 然后根据各自的价格范围,找出  max(match 关联的数量)
	 * 3. 若 match 的个数相同，分别找出相差最接近的价格
	 * @param morePrice
	 * @param inputMap
	 * @return
	 */
	private double analyzeCloselyPriceFromPriceGrouping(double[] morePrice , LinkedHashSet<CrestTroughTogetherTypeVO> tmpSet) {
		
		Map<Double, Map<Date,LinkedHashSet<CrestTroughTogetherTypeVO> > > priceCounterMap = 
			new HashMap<Double, Map<Date,LinkedHashSet<CrestTroughTogetherTypeVO> > >();
		Map<Double, Integer > priceCounter = new HashMap<Double, Integer >();
		
		for (double comparedPrice : morePrice) {
			
			double max = sdkHelperManager.getStockBlurLimitedPrice(comparedPrice, BlurRangeType.MAX, this.getStockCode() );
			double min = sdkHelperManager.getStockBlurLimitedPrice(comparedPrice, BlurRangeType.MIN, this.getStockCode() );

			Map<Date,LinkedHashSet<CrestTroughTogetherTypeVO> > dateMapSet = new HashMap<Date,LinkedHashSet<CrestTroughTogetherTypeVO> >();
			
			for (CrestTroughTogetherTypeVO kline : tmpSet) {
				
				if (kline.getPrice() <= max && kline.getPrice() >= min) {
					// --- count for date mapping --- 
					LinkedHashSet<CrestTroughTogetherTypeVO> itemSet = dateMapSet.get( kline.getDate() );
					if ( itemSet == null ) {
						itemSet = new LinkedHashSet<CrestTroughTogetherTypeVO>();
					}
					itemSet.add( kline );
					
					dateMapSet.put( kline.getDate() , itemSet);
				}
			}
			priceCounterMap.put( comparedPrice , dateMapSet);
			priceCounter.put( comparedPrice , dateMapSet.size() );
		}
		// ---- sort by counter ----
		priceCounter = sortPriceMapCounter(priceCounter);
		
		// ---- find the same top price ---
		Iterator< Map.Entry<Double, Integer> >  counterEntriesIter = priceCounter.entrySet().iterator();
		
		LinkedHashSet<Double> needScanPrice = new LinkedHashSet<Double>();
		
		Map.Entry<Double, Integer>  firstEntry = counterEntriesIter.next();
		int firstTopValue = firstEntry.getValue();
		needScanPrice.add( firstEntry.getKey() );
		
		while ( counterEntriesIter.hasNext() ) {
			Map.Entry<Double, Integer>  entry = counterEntriesIter.next();
			int currentValue = entry.getValue();
			if (  firstTopValue == currentValue ) {
				needScanPrice.add( entry.getKey()  );
			}
		}
		
		// --- min(diff from price) ---
		double outputPrice = 0;
		
		double lastAvgprice = 999999999;
		for (Iterator<Double>  needScanPriceIter = needScanPrice.iterator() ; needScanPriceIter.hasNext(); ) {
			double currentPrice = needScanPriceIter.next();
			
			Map<Date,LinkedHashSet<CrestTroughTogetherTypeVO> > dateMapCounter = priceCounterMap.get( currentPrice );
			Set<Map.Entry<Date,LinkedHashSet<CrestTroughTogetherTypeVO> >>   entriesSet = dateMapCounter.entrySet()  ;
			List<Double> subAllDifferent = new ArrayList<Double>();
			for (Map.Entry<Date,LinkedHashSet<CrestTroughTogetherTypeVO> >  entry :  entriesSet ) {
				Date refDate = entry.getKey();
				LinkedHashSet<CrestTroughTogetherTypeVO> sameDateSet =  entry.getValue();
				// --- find the close Min value ---
				CrestTroughTogetherTypeVO item = findMinCloselyItem(currentPrice , sameDateSet);
				subAllDifferent.add(  Math.abs( currentPrice - item.getPrice() ) );
			}
			double currentAvgPrice = getAverageDiffFromReferPrice( subAllDifferent );
			if (currentAvgPrice < lastAvgprice ) {
				lastAvgprice = currentAvgPrice;
				outputPrice = currentPrice;
			}
		}
		
		return outputPrice;
	}
	
	
	
	
	private static double getAverageDiffFromReferPrice(List<Double> subAllDifferent ) {
		double sutTotal = 0;
		for (Double single : subAllDifferent) {
			sutTotal += single;
		}
		
		double averge = sutTotal / subAllDifferent.size();
		return averge;
		
	}
	
	
	private static Map<Double,Integer> sortPriceMapCounter(Map<Double,Integer> togetherCounterMap ) {
		
		Set<Map.Entry<Double, Integer>>  entries = togetherCounterMap.entrySet();
		
		List<Map.Entry<Double, Integer>> entriesList = new Vector<Map.Entry<Double, Integer>>( entries );
		
		
		Collections.sort(entriesList, new Comparator<Map.Entry<Double, Integer>>() {
			@Override
			public int compare(Map.Entry<Double, Integer> arg0,
					Map.Entry<Double, Integer> arg1) {
				
				if ( arg0.getValue() > arg1.getValue() ) {
					return -1;
				} 
				else if (arg0.getValue() < arg1.getValue()) {
					return 1;
				}
				
				return 0;
			}
		});
		
		
		Map<Double,Integer> entriesMap = new LinkedHashMap<Double, Integer>();
		for (Map.Entry<Double, Integer>  entry : entriesList ) {
			entriesMap.put( entry.getKey() , entry.getValue() );
		}
		return entriesMap;
	}
	
	private static CrestTroughTogetherTypeVO findMinCloselyItem(double currentprice , LinkedHashSet<CrestTroughTogetherTypeVO> sameDateSet) {
		// --- defined same date set ---
		double minClose = 999999999;
		CrestTroughTogetherTypeVO output = null;
		for (CrestTroughTogetherTypeVO item : sameDateSet) {
			double currentPrice = Math.abs( currentprice - item.getPrice() );
			if (currentPrice < minClose ) {
				output = item;
				minClose = currentPrice;
			}
		}
		return output;
	}
	
	private static double[] pickOutcloselyPriceGrouping(double stanLevelPrice , double[] inputMorePrice ) {
		// --- defined more price ---
		List<Double> priceList = new ArrayList<Double>();
		for (int i = 0 ; i < inputMorePrice.length ; i++) {
			priceList.add( inputMorePrice[i] );
		}
		Collections.sort( priceList );
		
		// --- find the index ---
		int index = priceList.indexOf( stanLevelPrice );
		Integer preIndex = index - 1;
		Integer nextIndex = index + 1;
		
		int priceCounter = 1;
		if (preIndex < 0) {
			preIndex = null;
		} else {
			priceCounter+=1;			
		}
		if (nextIndex >= priceList.size()) {
			nextIndex = null;
		} else {
			priceCounter+=1;
		}
		double[] output = new double[priceCounter];
		int i = 0;
		if (preIndex != null) {
			output[i] = priceList.get( preIndex );
			i++;
		}
		output[i] = priceList.get( index );
		i++;
		if (nextIndex != null) {
			output[i] = priceList.get( nextIndex );
		}
		       

		return output;
	}
	
	@Resource
	private StockBusinessHelperManager sdkHelperManager;
	
	/**
	 * find some price match for compared Price
	 * @param comparedPrice
	 * @param listPrices
	 * @return
	 */
	private double[] searchMatchPrices(String stkcode , double comparedPrice , List<Double> listPrices) {

		double max = sdkHelperManager.getStockBlurLimitedPrice(comparedPrice, BlurRangeType.MAX, stkcode);
		double min = sdkHelperManager.getStockBlurLimitedPrice(comparedPrice, BlurRangeType.MIN, stkcode);

		List<Double> matchPrices = new Vector<Double>();		
		
		for (Double p : listPrices) {
			
			if (p <= max && p >= min) {
				matchPrices.add(p);
			}
		}		
		
		double[] output = new double[ matchPrices.size() ];
		int index = 0;		
		for (Iterator<Double> pattIter = matchPrices.iterator() ; pattIter.hasNext(); ) {
			
			double currentPrice = pattIter.next();
			
			output[index] = currentPrice;
		
			pattIter.remove();
			
			index = index + 1;
		}		
		
		return output;
	}	
	
}
